package org.quickstep.application;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Arrays;
import java.util.Collection;

import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;

import org.apache.log4j.Logger;
import org.quickstep.application.action.ActionApplicationExit;
import org.quickstep.application.action.ActionGroup;
import org.quickstep.application.action.IActionGroup;
import org.quickstep.application.util.MenuBuilder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;


public final class Application implements BeanFactoryAware {

	private static final Logger LOG = Logger.getLogger(Application.class);
	
	private static Application instance;
	
	private final WindowAdapter windowListener = new WindowAdapter() {

		@Override
		public void windowClosing(WindowEvent e) {
			LOG.info("Application frame is closing...");
			tryToExit();
		}

		@Override
		public void windowClosed(WindowEvent e) {
			LOG.info("Application frame is closed");
			exit(0);
		}
		
	};

	private BeanFactory beanFactory;
	
	private JFrame frame;
	
	private IApplicationAdvisor advisor; 
	
	private EventListenerList eventListeners = new EventListenerList();
	
	private Collection<IActionGroup> mainMenuActions; 

	
	protected Application() {
		instance = this;
	}
	
	public static final Application getInstance() {
		return instance;
	}
	

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = beanFactory;
	}
	
	public BeanFactory getBeanFactory() {
		return beanFactory;
	}
	
	public void setAdvisor(IApplicationAdvisor advisor) {
		this.advisor = advisor;
	}
	
	public IApplicationAdvisor getAdvisor() {
		return advisor;
	}
	
	public void setExitListeners( Collection<IApplicationExitListener> exitListsners ) {
		for ( IApplicationExitListener l: exitListsners ) {
			this.eventListeners.add(IApplicationExitListener.class, l);
		}
	}
	
	public void setMainMenuActions(Collection<IActionGroup> mainMenuActions) {
		this.mainMenuActions = mainMenuActions;
	}
	
	protected void init() {
		LOG.info("Initializing...");
		
		// should run on EDT
		SwingUtilities.invokeLater( new Runnable() {
			
			@Override
			public void run() {
				
				advisor.startup();
				frame = buildApplicationFrame();
				LOG.info("Application frame is initialized");
				
				advisor.ready();
				frame.setVisible(true);
			}
			
		}); 
		
	}
	
	//TODO Move out to builder class? 
	private JFrame buildApplicationFrame() {
		
		final JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frame.setSize(800,600);
		frame.setLocationRelativeTo(null);
		frame.addWindowListener( windowListener );
		
		//frame.setIconImage(image)
		
		// Build main menu
		if ( mainMenuActions.size() > 0 ) {
			frame.setJMenuBar( buildMainMenu() );
		}
		
		return frame;
	}
	
	//TODO Move out to builder class?
	private JMenuBar buildMainMenu() {
		JMenuBar menuBar = new JMenuBar();
		ActionGroup fileMenu = new ActionGroup("File", new ActionApplicationExit());
		MenuBuilder.buildMenuBar(menuBar, Arrays.asList( fileMenu ), false );
		return menuBar;
	}
	
	/**
	 * Exits the application if all the exist listeners agree with it
	 */
	public void tryToExit() {
		
		if ( canExit() ) {
			if ( frame != null )  {
				frame.dispose(); // frame default close operation should be DO_NOTHING_ON_CLOSE
			} else {
				exit(0);
			}
		}
		
	}
	
	/**
	 * Exist application immediately. Notifies application advisor
	 * @param exitContext
	 */
	public void exit( int exitContext ) {
		
		IApplicationAdvisor advisor = getAdvisor();
		if ( advisor != null ) advisor.shutdown();
		
		Runtime.getRuntime().exit(exitContext);
	}
	
	
	private boolean canExit() {
		for ( IApplicationExitListener listener: eventListeners.getListeners(IApplicationExitListener.class) ) {
			if ( !listener.canExit()) return false;
		}
		return true;
	}

	
}
